package com.huilian.tool.applet.wechat.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.huilian.tool.applet.wechat.service.WeChatAppletService;
import com.huilian.tool.common.config.AppletWeChatConfig;
import com.huilian.tool.common.utils.AES;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class WeChatAppletServiceImpl implements WeChatAppletService {


    @Resource
    private RestTemplate restTemplate;
    @Resource
    private AppletWeChatConfig appletWeChatConfig;
    @Resource
    private RedisTemplate<String, Serializable> redisCacheTemplate;

    private static final String grant_type = "authorization_code";
    // 微信小程序获取用户信息
    private String JS_CODE2_SESSION = "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=%s";

    //获取手机号
    private final String GET_USER_PHONE_NUMBER = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=%s";

    private final String GET_ACCESS_TOKEN = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";

    @Override
    public String getAccessToken(String redisKey) throws Exception {
        String accessToken = (String) redisCacheTemplate.opsForValue().get(redisKey);

        if (StringUtils.isEmpty(accessToken)) {
            final String url = String.format(GET_ACCESS_TOKEN, appletWeChatConfig.getAppId(), appletWeChatConfig.getAppSecret());
            JSONObject jsonObject = restTemplate.getForObject(url, JSONObject.class);
            if (!StringUtils.isEmpty(jsonObject.getString("errcode"))) {
                throw new Exception("获取accessToken失败！" + jsonObject);
            }
            accessToken = jsonObject.getString("access_token");
            redisCacheTemplate.opsForValue().set(redisKey, accessToken, 7100, TimeUnit.SECONDS);
        }
        return accessToken;
    }

    @Override
    public String getAccessToken(String appId, String appSecret, String redisKey) throws Exception {
        String accessToken = (String) redisCacheTemplate.opsForValue().get(redisKey);
        if (StringUtils.isEmpty(accessToken)) {
            final String url = String.format(GET_ACCESS_TOKEN, appId, appSecret);
            JSONObject jsonObject = restTemplate.getForObject(url, JSONObject.class);
            if (!StringUtils.isEmpty(jsonObject.getString("errcode"))) {
                throw new Exception("获取accessToken失败！" + jsonObject);
            }
            accessToken = jsonObject.getString("access_token");
            redisCacheTemplate.opsForValue().set(redisKey, accessToken, 7100, TimeUnit.SECONDS);
        }
        return accessToken;
    }


    /**
     * 微信小程序登录
     *
     * @param code
     * @return
     */
    @Override
    public String getJsCode2Session(String code) {

        final String url = String.format(JS_CODE2_SESSION, appletWeChatConfig.getAppId(), appletWeChatConfig.getAppSecret(), code, grant_type);
        /**
         * 返回示例：
         * {
         *   "session_key": "Ltwj1cnI*****9QBTiytIpw==",
         *   "openid": "ohpyk5Uwgt2*****SwcWfLVqg",
         *   "unionid": "oCJdU1YVJP*****GII1G0t6nA"
         * }
         */
        return restTemplate.getForObject(url, String.class);
    }

    @Override
    public String getJsCode2Session(String code, String appId, String appSecret) {

        final String url = String.format(JS_CODE2_SESSION, appId, appSecret, code, grant_type);
        /**
         * 返回示例：
         * {
         *   "session_key": "Ltwj1cnI*****9QBTiytIpw==",
         *   "openid": "ohpyk5Uwgt2*****SwcWfLVqg",
         *   "unionid": "oCJdU1YVJP*****GII1G0t6nA"
         * }
         */
        return restTemplate.getForObject(url, String.class);
    }

    /**
     * 解密获取用户信息
     *
     * @param iv
     * @param encryptedData
     * @param sessionKey
     * @return
     */
    @Override
    public String getUserInfoAndDecode(String iv, String encryptedData, String sessionKey) throws Exception {
        byte[] resultByte = AES.decrypt(Base64.decodeBase64(encryptedData), Base64.decodeBase64(sessionKey), Base64.decodeBase64(iv));
        /**
         * 返回示例：
         * {
         *   "nickName": "Mr Chen",
         *   "gender": 0,
         *   "language": "zh_CN",
         *   "city": "",
         *   "province": "",
         *   "country": "",
         *   "avatarUrl": "https://thirdwx.qlogo.cn/mmopen/vi_32/522q0DDQSDezIVY3vvz51Jp6h8QHicQBIUiazDTc04iaicrgrh4z4fVeibS8J6olW5tsQ6Dxe0YEhN70mVvop13Rib5g/132",
         *   "watermark": {
         *     "timestamp": 1669271134,
         *     "appid": "wx497d*****fb41bd0"
         *   }
         * }
         */
        return new String(resultByte, StandardCharsets.UTF_8);
    }

    @Override
    public String getUserPhoneNumber(String code, String redisKey) throws Exception {
        final String url = String.format(GET_USER_PHONE_NUMBER, this.getAccessToken(redisKey));
        JSONObject param = new JSONObject();
        param.put("code", code);
        JSONObject jsonObject = restTemplate.postForObject(url, param, JSONObject.class);
        log.info("jsonObject:" + JSON.toJSONString(jsonObject));
        if (jsonObject.getInteger("errcode") == 0) {
            return jsonObject.getJSONObject("phone_info").getString("phoneNumber");
        } else {
            throw new Exception(jsonObject.getString("errmsg"));
        }

    }

    @Override
    public String getUserPhoneNumber(String code, String appId, String appSecret, String redisKey) throws Exception {
        final String url = String.format(GET_USER_PHONE_NUMBER, this.getAccessToken(appId, appSecret, redisKey));
        JSONObject param = new JSONObject();
        param.put("code", code);
        JSONObject jsonObject = restTemplate.postForObject(url, param, JSONObject.class);
        log.info("jsonObject:" + JSON.toJSONString(jsonObject));
        if (jsonObject.getInteger("errcode") == 0) {
            return jsonObject.getJSONObject("phone_info").getString("phoneNumber");
        } else {
            throw new Exception(jsonObject.getString("errmsg"));
        }
    }
}
